# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2023/4/25 16:49 
# @Author : zxl
# @Email : zhangxl_njit@163.com
# @File : reinit_instance.py
# @Version: open-planner 2.0
"""
文件说明：重新定义流实例

"""
import math
from copy import deepcopy

from OpenPlanner.core.src.op_class import OPClass

from OpenPlanner.usr.xz_plan.input.xz_plan_algo_input import *
from OpenPlanner.util.compute_hyper_period import compute_hyper_period


class TT_Node:
    def __init__(self, node_id, win):
        self.node_id = node_id
        self.win = win


# 路径中的发送端
class Talker(TT_Node):
    def __init__(self, node_id, win, outport, slot=0):
        super(Talker, self).__init__(node_id, win)
        self.outport = outport
        self.slot = slot


# 路径中的交换机端
class Bridge(TT_Node):
    def __init__(self, node_id, win, inport, outport, inslot=0, outslot=0):
        super(Bridge, self).__init__(node_id, win)
        self.inport = inport
        self.outport = outport
        self.inslot = inslot
        self.outslot = outslot


class Listener(TT_Node):
    def __init__(self, node_id, win, inport, slot=0):
        super(Listener, self).__init__(node_id, win)
        self.inport = inport
        self.slot = slot


class Path:
    def __init__(self, talker, listener, bridges=[]):
        '''
        Args:
            talker: 流实例的起始节点
            listener: 流实例的终止节点
            bridges: 流实例经过的交换节点
        '''
        self.talker = talker
        self.listener = listener
        self.bridges = bridges


# 重新定义instances集合
def instance_reinit_hook(op_obj: OPClass):
    stream_obj_set = op_obj.algo_input.stream_array
    link_obj_set = op_obj.algo_input.link_array
    algo_param_dict = op_obj.algo_param
    # 重新组织instance_array
    op_obj.algo_input.instance_array = generate_instance_obj_set(stream_obj_set, link_obj_set, algo_param_dict)


# 流实例预处理
def generate_instance_obj_set(stream_obj_set, link_obj_set, algo_param_dict):
    global huge_slot
    global TT_len
    global buf_num
    global slot_num
    huge_slot = int(algo_param_dict['huge_slot'])
    TT_len = int(algo_param_dict['TT_len'])
    buf_num = int(algo_param_dict['buf_num'])
    slot_num = int(algo_param_dict['slot_num'])
    period_set = []
    for st in stream_obj_set:
        period_set.append(st.period)
    hyper_period = compute_hyper_period(*period_set)
    # 一个流有一个多个周期的流实例，全局产生的流实例为二维数组。
    # 如[[流1的周期1流实例，流1的周期2流实例。。。]，[流2的周期1流实例，流2的周期2流实例]]
    stream_instance_obj_set = []
    for st in stream_obj_set:
        # # 一个流有多个周期的流实例，一个周期视为一个流实例
        stream_instance_obj_per_stream = []
        period = st.period
        # 超周期内流得第0个流实例
        # 首先算出第0个流实例的talker和listener的初始时间范围
        talker_dict = st.opts['talker']
        # talker的时间范围
        tx_win = talker_dict['win']
        listeners_dict = st.opts['listeners']
        # listener的初始时间范围数组，同一个流的不同流可能有多个listener
        rx_win_set = []
        for listener in listeners_dict:
            # listener_hs_scope = _hs_trans(listener['win'], huge_slot)
            rx_win = listener['listener']['win']
            rx_win_set.append(rx_win)

        # 第一层循环
        for seq in range(math.ceil(hyper_period / period)):
            # 算出第（n-1）个周期内的talker和listener的时间范围
            # 相同Talker，不同Listener，有不同的路径。一个seq的实例包含Path对象数组
            # 一个流的不同周期视为不同的流实例。通过流对象生成流实例对象
            # 重新定义流实例
            stream_instance_obj = HugeSlotInstance(seq, st.stream_id, st.size, st.period)
            path_set = []
            routes = [st.route]
            # 不同周期的talker和listeners的时间窗口
            tx_win_seq = _range_by_seq(tx_win, seq, period)
            # 接收端的时间范围和routes数组相对应
            rx_win_seq_set = []
            for rx_win in rx_win_set:
                rx_win_seq = _range_by_seq(rx_win, seq, period)
                rx_win_seq_set.append(rx_win_seq)

            # 时间窗口转换成宏时隙窗口
            talker_hs_scope = _hs_trans(tx_win_seq, huge_slot)
            # 生成该周期内的Talker对象
            talker_obj = Talker(talker_dict['node_id'], talker_hs_scope, link_obj_set[routes[0][0]].src_port)
            listener_obj_set = []

            # 生成该周期内的Listener对象数组
            for i in range(0, len(listeners_dict)):
                # 对应的第i条route路径
                route = routes[i]
                listener_dict = listeners_dict[i]
                listen_hs_scope = _hs_trans(rx_win_seq_set[i], huge_slot)
                # 路径的最后一个链路的dst_port即为listener的inport
                listener_obj = Listener(listener_dict['listener']['node_id'], listen_hs_scope,
                                        link_obj_set[route[-1]].dst_port)
                listener_obj_set.append(listener_obj)

            # 第2层循环
            # 一个流可能有多条路径，生成多个Path对象
            for i in range(0, len(routes)):
                # 生成该路径下的Path对象
                path = Path(talker_obj, listener_obj_set[i])
                # 初始化该路径的交换节点对象数组
                bridges = []
                # 对应的第i条route路径
                route = routes[i]
                # 生成Bridge的对象
                # hop是该流沿着路由的第几跳，对应link_obj_set的第hop个link_obj
                # 第3层循环
                # 第一个link开始，从link求解出bridge
                next = 0
                while next < len(route) - 1:
                    node_id = link_obj_set[route[next]].dst_node
                    # 时间范围初始为空
                    win = [0] * 2
                    inport = link_obj_set[route[next]].dst_port
                    next += 1
                    outport = link_obj_set[route[next]].src_port
                    bridge = Bridge(node_id, win, inport, outport)
                    bridges.append(bridge)
                path.bridges = bridges
                # 多条路由生成多条path
                path_set.append(path)
            stream_instance_obj.path_set = path_set
            stream_instance_obj_per_stream.append(stream_instance_obj)
        stream_instance_obj_set.append(stream_instance_obj_per_stream)
    stream_instance_obj_set = time_window_revise(stream_instance_obj_set)
    return stream_instance_obj_set


# 关于流实例的时间窗口修正，对外暴露
# 因为修正子子流的时间窗口，所以stream_instance_obj_set
def time_window_revise(stream_instance_obj_set):
    # 此处默认所有交换机都是一样的缓存值，所有的终端节点都是一样的缓存值
    sw_buf_num = buf_num
    rx_buf_num = buf_num
    # stream_instance_obj_per_stream为流的各周期流实例的数组
    # stream_instance_obj 为流实例对象，每个流的每个周期为1个流实例对象
    # paths 为每个流实例中的路径数组，因为一个流可能有多个listener
    stream_instance_obj_set_copy = deepcopy(stream_instance_obj_set)
    for stream_instance_obj_per_stream in stream_instance_obj_set_copy:
        for stream_instance_obj in stream_instance_obj_per_stream:
            path_set = stream_instance_obj.path_set
            _fix_win(path_set, sw_buf_num, rx_buf_num)
    return stream_instance_obj_set_copy


# 时间窗口修正
def _fix_win(path_set, sw_buf_num, rx_buf_num):
    for path in path_set:
        talker = path.talker
        listener = path.listener
        bridges = path.bridges
        tx_win = talker.win[:]
        rx_win = listener.win[:]
        ND_min = len(bridges) + 1
        ND_max = len(bridges) * sw_buf_num + rx_buf_num
        if tx_win[1] + ND_max < rx_win[0] or tx_win[0] + ND_min > rx_win[1]:
            # print("talker和listener窗口约束不合理！！")
            raise RuntimeError('窗口约束不合理，导致该算法无解')
        #   接收端窗口修正
        shed_rx_win = _fix_rx_win(ND_min, ND_max, rx_win, tx_win)
        path.listener.win = shed_rx_win[:]
        # path.listener.slot = shed_rx_win[:][0]
        #   发送端窗口修正
        shed_tx_win = _fix_tx_win(ND_min, ND_max, rx_win, tx_win)
        path.talker.win = shed_tx_win[:]
        # path.talker.slot = shed_tx_win[:][0]
        #   交换节点窗口修正
        _fix_sw_win(sw_buf_num, rx_buf_num, path)


def _fix_rx_win(ND_min, ND_max, rx_win, tx_win):
    shed_rx_win = [0] * 2
    shed_rx_win[0] = max(rx_win[0], tx_win[0] + ND_min)
    shed_rx_win[1] = min(rx_win[1], tx_win[1] + ND_max)

    # if rx_win[0] != shed_rx_win[0] or rx_win[1] != shed_rx_win[1]:
    #     print("rx win has revised!")
    return shed_rx_win


def _fix_tx_win(ND_min, ND_max, rx_win, tx_win):
    shed_tx_win = [0] * 2
    shed_tx_win[0] = max(tx_win[0], rx_win[0] - ND_max)
    shed_tx_win[1] = min(tx_win[1], rx_win[1] - ND_min)
    # if tx_win[0] != shed_tx_win[0] or tx_win[1] != shed_tx_win[1]:
    #     print("tx win has revised!")
    return shed_tx_win


def _fix_sw_win(sw_buf_num, rx_buf_num, path):
    talker = path.talker
    listener = path.listener
    bridges = path.bridges
    shed_tx_win = talker.win[:]
    shed_rx_win = listener.win[:]
    # 记录上一个交换节点窗口，初始值为可调度发送窗口
    pre_tx_win = shed_tx_win[:]
    for i in range(0, len(bridges)):
        bridge = bridges[i]
        rsn = len(bridges) - i - 1
        shed_sx_win_min = max(pre_tx_win[0] + 1, shed_rx_win[0] - rsn * sw_buf_num - rx_buf_num)
        shed_sx_win_max = min(shed_sx_win_min + sw_buf_num - 1, shed_rx_win[1] - rsn - 1)
        pre_tx_win[0] = shed_sx_win_min
        pre_tx_win[1] = shed_sx_win_max
        # if bridge.win[0] != shed_sx_win_min or bridge.win[1] != shed_sx_win_max:
        #     print("sw win has revised!")
        bridge.win[0] = shed_sx_win_min
        bridge.win[1] = shed_sx_win_max
        # 调整时隙
        # bridge.outslot = shed_sx_win_min


def _hs_trans(time_range, hs):
    t_min = time_range[0]
    t_max = time_range[1]
    # 返回一个新数组，不改变原先时间窗口
    win_trans = [0] * 2
    win_trans[0] = math.ceil(t_min / hs)
    if t_max % hs == 0:
        win_trans[1] = t_max / hs
    elif t_max % hs < TT_len:
        win_trans[1] = math.floor(t_max / hs) - 1
    else:
        win_trans[1] = math.ceil(t_max / hs) - 1
    return win_trans


# 按照序号计算时间范围
def _range_by_seq(win, seq, period):
    t_min = win[0]
    t_max = win[1]
    win_trans = [0] * 2
    win_trans[0] = t_min + seq * period
    win_trans[1] = t_max + seq * period
    return win_trans
